Conversation
📝 WalkthroughWalkthroughThis PR refactors KakaoPayController to replace injected URL configuration with a hardcoded FRONT_DOMAIN constant and converts cancel/fail endpoints to use HTTP redirects instead of ApiResponse returns. It introduces a new TempTicketExpireScheduler that automatically cancels payments for tickets pending longer than 15 minutes. KakaoPayBusinessService.stopPayment now returns playId, and TempTicketRepository gains a specialized query method for expired tickets. Changes
Sequence Diagram(s)sequenceDiagram
participant Scheduler as TempTicketExpireScheduler
participant Repo as TempTicketRepository
participant DB as Database
participant Service as KakaoPayBusinessService
Scheduler->>Scheduler: Calculate expiration threshold<br/>(Seoul time - 15 min)
activate Scheduler
Scheduler->>Repo: findExpiredPendingTickets(expireTime)
activate Repo
Repo->>DB: Query PENDING tickets<br/>created before threshold
activate DB
DB-->>Repo: Return expired tickets
deactivate DB
Repo-->>Scheduler: List<TempTicket>
deactivate Repo
alt Tickets found
Scheduler->>Scheduler: Iterate over each ticket
Scheduler->>Service: stopPayment(partnerOrderId)
activate Service
Service->>DB: Restore inventory &<br/>update status
activate DB
DB-->>Service: Success
deactivate DB
Service-->>Scheduler: Return playId
deactivate Service
Scheduler->>Scheduler: Log successful expiration
else No tickets found
Scheduler->>Scheduler: Exit early
end
deactivate Scheduler
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java (1)
36-47:⚠️ Potential issue | 🟠 MajorMissing error handling in
approve— a failure results in a raw 500 error instead of a user-friendly redirect.Unlike
cancelandfail, theapproveendpoint has no try-catch. IfcompletePaymentthrows, the user sees a server error page. Wrap it consistently to redirect with an error indicator.🐛 Proposed fix
public void approve(`@Parameter`(description = "ticketId 입니다") `@RequestParam`("partner_order_id") String partnerOrderId, `@RequestParam`("pg_token") String pgToken, HttpServletResponse response) throws IOException { + try { KakaoPayResultResponseDTO result = kakaoPayBusinessService.completePayment(partnerOrderId, pgToken); response.sendRedirect( FRONT_DOMAIN + "/ticketing/" + result.getAmateurShowId() + "?payment=success" ); + } catch (Exception e) { + log.error("Payment approval redirect failed for partnerOrderId={}", partnerOrderId, e); + response.sendRedirect(FRONT_DOMAIN + "?payment=fail"); + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java` around lines 36 - 47, The approve method lacks error handling: wrap the call to kakaoPayBusinessService.completePayment(partnerOrderId, pgToken) inside a try-catch in the approve(...) controller, and on success keep the existing response.sendRedirect(FRONT_DOMAIN + "/ticketing/" + result.getAmateurShowId() + "?payment=success"); but on any exception catch it and redirect the user to the same ticketing page (or a safe fallback) with an error query like "?payment=fail" (or "?payment=error") to mirror cancel/fail behavior; ensure you catch Exception, log the error, and still call response.sendRedirect with the error indicator so users never see a raw 500.
🧹 Nitpick comments (1)
src/main/java/cc/backend/kakaoPay/service/KakaoPayBusinessService.java (1)
190-214: Consider usingfindWithTicketAndShowByIdto avoid lazy-loading overhead.Line 194 uses
findById, but line 197 immediately navigatestempTicket.getAmateurRound().getAmateurShow().getId()— a chain of lazy associations. Since the repository already hasfindWithTicketAndShowByIdwith an@EntityGraphthat eagerly fetchesamateurRoundandamateurTicket.amateurShow, switching to it would eliminate the extra queries (especially relevant since the scheduler calls this method repeatedly).♻️ Proposed refactor
- TempTicket tempTicket = tempTicketRepository.findById(ticketId) - .orElseThrow(() -> new GeneralException(ErrorStatus.TEMP_TICKET_NOT_FOUND)); + TempTicket tempTicket = tempTicketRepository.findWithTicketAndShowById(ticketId) + .orElseThrow(() -> new GeneralException(ErrorStatus.TEMP_TICKET_NOT_FOUND));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/cc/backend/kakaoPay/service/KakaoPayBusinessService.java` around lines 190 - 214, The stopPayment method currently uses tempTicketRepository.findById which triggers lazy-loading when accessing tempTicket.getAmateurRound().getAmateurShow().getId(); replace the findById call with tempTicketRepository.findWithTicketAndShowById(partnerOrderIdLong) (or the equivalent signature) so the amateurRound and amateurShow are eagerly fetched via the repository's `@EntityGraph`, preserving the existing orElseThrow(()-> new GeneralException(ErrorStatus.TEMP_TICKET_NOT_FOUND)) behavior; keep the rest of the logic (checking ReservationStatus, amateurRoundsRepository.increaseStock(...), and tempTicket.updateReservationStatus(...)) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java`:
- Around line 50-62: The cancel and fail GET handlers (methods cancel and fail)
call kakaoPayBusinessService.stopPayment using an unauthenticated
partner_order_id, allowing attackers to expire tickets by guessing IDs; modify
preparePayment to emit a short-lived signed token/HMAC tied to partner_order_id
(and optionally timestamp/nonce) and include it in the redirect URL, then
validate that token in the cancel and fail endpoints before calling stopPayment
(reject or ignore requests with missing/invalid/expired tokens); ensure
validation uses a server-side secret and check signature + expiry to prevent
replay.
- Around line 52-62: Add structured logging to KakaoPayController by annotating
the class with a logging annotation (e.g., `@Slf4j`) and replace any
e.printStackTrace() calls in the cancel method (and the similar block at lines
67-77) with log.error statements that include a clear message and the exception
(e.g., log.error("Failed to cancel payment for partnerOrderId={}, redirecting to
front", partnerOrderId, e)); ensure you import/use the same logger across the
class so exceptions are captured by the app's logging framework.
- Around line 23-25: Replace the hardcoded FRONT_DOMAIN constant with an
externalized property: remove the private static final String FRONT_DOMAIN =
"https://seeatheater.site" and restore an `@Value-injected` field in
KakaoPayController (e.g., add `@Value`("${front.domain}") private String
FRONT_DOMAIN) so the controller reads the URL from configuration/environment;
add the property mapping in application.yml (front.domain: ${FRONT_DOMAIN}) or
ensure FRONT_DOMAIN is provided as an env var, and update any references to use
the injected FRONT_DOMAIN field.
In `@src/main/java/cc/backend/kakaoPay/service/KakaoPayBusinessService.java`:
- Around line 190-192: The code currently calls Long.valueOf(partnerOrderId) in
stopPayment which will throw NumberFormatException for non-numeric input; wrap
the parse in a try-catch, catch NumberFormatException, log a clear message
including the offending partnerOrderId and the exception, and then return a safe
failure (e.g., throw an IllegalArgumentException or a domain-specific exception)
instead of allowing the unchecked exception to propagate silently; update
callers if needed so the scheduler and controller handle the returned/raised
error consistently.
In `@src/main/java/cc/backend/scheduler/TempTicketExpireScheduler.java`:
- Around line 27-28: The scheduler in TempTicketExpireScheduler uses
LocalDateTime.now(ZoneId.of("Asia/Seoul")) to compute expireTime while persisted
createdAt in BaseEntity is timezone-unaware (JVM default/UTC), causing a 9-hour
mismatch; change the scheduler to use the same timezone as persistence (replace
LocalDateTime.now(ZoneId.of("Asia/Seoul")) and expireTime calculation with
LocalDateTime.now() if your JVM/database uses UTC) or alternatively configure
hibernate.jdbc.time_zone to a fixed zone and make both BaseEntity persistence
and TempTicketExpireScheduler use that same zone consistently; update any
related tests/comments to reflect the chosen approach.
In `@src/main/java/cc/backend/ticket/repository/TempTicketRepository.java`:
- Around line 39-45: The JPQL in TempTicketRepository.findExpiredPendingTickets
uses the string literal 'PENDING' which is brittle; update the query to
reference the enum constant directly (e.g., use the fully qualified enum
reference like YourReservationStatusEnumType.PENDING in the WHERE clause) so the
JPQL compares against the enum constant rather than a string, or if you prefer a
reusable API, change the method signature to accept a ReservationStatus
parameter (ReservationStatus status, LocalDateTime expireTime) and use that
parameter in the WHERE clause and pass ReservationStatus.PENDING from the
scheduler; adjust imports and tests accordingly.
---
Outside diff comments:
In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java`:
- Around line 36-47: The approve method lacks error handling: wrap the call to
kakaoPayBusinessService.completePayment(partnerOrderId, pgToken) inside a
try-catch in the approve(...) controller, and on success keep the existing
response.sendRedirect(FRONT_DOMAIN + "/ticketing/" + result.getAmateurShowId() +
"?payment=success"); but on any exception catch it and redirect the user to the
same ticketing page (or a safe fallback) with an error query like
"?payment=fail" (or "?payment=error") to mirror cancel/fail behavior; ensure you
catch Exception, log the error, and still call response.sendRedirect with the
error indicator so users never see a raw 500.
---
Nitpick comments:
In `@src/main/java/cc/backend/kakaoPay/service/KakaoPayBusinessService.java`:
- Around line 190-214: The stopPayment method currently uses
tempTicketRepository.findById which triggers lazy-loading when accessing
tempTicket.getAmateurRound().getAmateurShow().getId(); replace the findById call
with tempTicketRepository.findWithTicketAndShowById(partnerOrderIdLong) (or the
equivalent signature) so the amateurRound and amateurShow are eagerly fetched
via the repository's `@EntityGraph`, preserving the existing orElseThrow(()-> new
GeneralException(ErrorStatus.TEMP_TICKET_NOT_FOUND)) behavior; keep the rest of
the logic (checking ReservationStatus,
amateurRoundsRepository.increaseStock(...), and
tempTicket.updateReservationStatus(...)) unchanged.
| // private static final String FRONT_DOMAIN = "http://localhost:5173"; | ||
| private static final String FRONT_DOMAIN = "https://seeatheater.site"; |
There was a problem hiding this comment.
Hardcoded production URL replaces previously externalized configuration — this breaks environment portability.
Replacing the @Value-injected URL with a hardcoded "https://seeatheater.site" means local development, staging, and other environments all redirect to production. The commented-out localhost line on line 24 confirms this is being toggled manually, which is error-prone and could easily leak into production with the wrong value.
Re-externalize this to application.yml (or environment variable), as it was before:
🐛 Proposed fix
In application.yml, add:
front:
domain: ${FRONT_DOMAIN}In the controller:
-// private static final String FRONT_DOMAIN = "http://localhost:5173";
- private static final String FRONT_DOMAIN = "https://seeatheater.site";
+ `@Value`("${front.domain}")
+ private String frontDomain;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java` around
lines 23 - 25, Replace the hardcoded FRONT_DOMAIN constant with an externalized
property: remove the private static final String FRONT_DOMAIN =
"https://seeatheater.site" and restore an `@Value-injected` field in
KakaoPayController (e.g., add `@Value`("${front.domain}") private String
FRONT_DOMAIN) so the controller reads the URL from configuration/environment;
add the property mapping in application.yml (front.domain: ${FRONT_DOMAIN}) or
ensure FRONT_DOMAIN is provided as an env var, and update any references to use
the injected FRONT_DOMAIN field.
| @GetMapping("/cancel") | ||
| @Operation(summary = "카카오페이 결제 중단 취소 (자동 호출)", description = "결제 중단 시 카카오 서버에서 cancel_url로 자동 호출되는 API입니다. 직접 호출하지 마세요.") | ||
| public ApiResponse<String> cancel(@RequestParam("partner_order_id") String partnerOrderId) { | ||
| public void cancel(@RequestParam("partner_order_id") String partnerOrderId, HttpServletResponse response) throws IOException{ | ||
| try { | ||
| kakaoPayBusinessService.stopPayment(partnerOrderId); | ||
| return ApiResponse.onSuccess("결제가 취소되었습니다."); | ||
| } catch (NumberFormatException e) { | ||
| return ApiResponse.onFailure("INVALID_ORDER_ID", "유효하지 않은 주문번호입니다.", null); | ||
| Long playId = kakaoPayBusinessService.stopPayment(partnerOrderId); | ||
| response.sendRedirect( | ||
| FRONT_DOMAIN + "/ticketing/" + playId + "?payment=cancel" | ||
| ); | ||
| } catch (Exception e) { | ||
| e.printStackTrace(); | ||
| response.sendRedirect(FRONT_DOMAIN); | ||
| } | ||
| } |
There was a problem hiding this comment.
Unauthenticated cancel and fail endpoints allow any caller to expire arbitrary pending tickets.
These GET endpoints accept a partner_order_id without authentication and call stopPayment, which restores inventory and marks the ticket as EXPIRED. If an attacker guesses or enumerates ticket IDs, they can force-expire other users' in-flight payments.
While the retrieved learning confirms these are browser redirect URLs (not server-to-server), consider adding a lightweight verification — e.g., a signed token or HMAC in the redirect URL generated during preparePayment, validated here before calling stopPayment.
Also applies to: 65-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java` around
lines 50 - 62, The cancel and fail GET handlers (methods cancel and fail) call
kakaoPayBusinessService.stopPayment using an unauthenticated partner_order_id,
allowing attackers to expire tickets by guessing IDs; modify preparePayment to
emit a short-lived signed token/HMAC tied to partner_order_id (and optionally
timestamp/nonce) and include it in the redirect URL, then validate that token in
the cancel and fail endpoints before calling stopPayment (reject or ignore
requests with missing/invalid/expired tokens); ensure validation uses a
server-side secret and check signature + expiry to prevent replay.
| public void cancel(@RequestParam("partner_order_id") String partnerOrderId, HttpServletResponse response) throws IOException{ | ||
| try { | ||
| kakaoPayBusinessService.stopPayment(partnerOrderId); | ||
| return ApiResponse.onSuccess("결제가 취소되었습니다."); | ||
| } catch (NumberFormatException e) { | ||
| return ApiResponse.onFailure("INVALID_ORDER_ID", "유효하지 않은 주문번호입니다.", null); | ||
| Long playId = kakaoPayBusinessService.stopPayment(partnerOrderId); | ||
| response.sendRedirect( | ||
| FRONT_DOMAIN + "/ticketing/" + playId + "?payment=cancel" | ||
| ); | ||
| } catch (Exception e) { | ||
| e.printStackTrace(); | ||
| response.sendRedirect(FRONT_DOMAIN); | ||
| } | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Replace e.printStackTrace() with proper logging.
e.printStackTrace() writes to stderr and bypasses the application's logging framework (log level, format, aggregation). The class doesn't even have @Slf4j. Use a logger instead so these errors are captured in your structured logs.
♻️ Proposed fix
Add @Slf4j to the class, then:
} catch (Exception e) {
- e.printStackTrace();
+ log.error("Payment cancel failed for partnerOrderId={}", partnerOrderId, e);
response.sendRedirect(FRONT_DOMAIN);
} } catch (Exception e) {
- e.printStackTrace();
+ log.error("Payment fail handling failed for partnerOrderId={}", partnerOrderId, e);
response.sendRedirect(FRONT_DOMAIN);
}Also applies to: 67-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/kakaoPay/controller/KakaoPayController.java` around
lines 52 - 62, Add structured logging to KakaoPayController by annotating the
class with a logging annotation (e.g., `@Slf4j`) and replace any
e.printStackTrace() calls in the cancel method (and the similar block at lines
67-77) with log.error statements that include a clear message and the exception
(e.g., log.error("Failed to cancel payment for partnerOrderId={}, redirecting to
front", partnerOrderId, e)); ensure you import/use the same logger across the
class so exceptions are captured by the app's logging framework.
| public Long stopPayment(String partnerOrderId) { | ||
|
|
||
| Long ticketId = Long.valueOf(partnerOrderId); |
There was a problem hiding this comment.
Long.valueOf on an invalid partnerOrderId will throw an unchecked NumberFormatException.
If partnerOrderId is not a valid numeric string (e.g., corrupted or tampered input), Long.valueOf on line 192 throws NumberFormatException, which propagates unhandled. In the controller's cancel/fail endpoints this is caught by the generic catch (Exception e), but in the scheduler path it could log-and-continue. Still, a more explicit handling or logging of this parse step would make debugging easier.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/kakaoPay/service/KakaoPayBusinessService.java`
around lines 190 - 192, The code currently calls Long.valueOf(partnerOrderId) in
stopPayment which will throw NumberFormatException for non-numeric input; wrap
the parse in a try-catch, catch NumberFormatException, log a clear message
including the offending partnerOrderId and the exception, and then return a safe
failure (e.g., throw an IllegalArgumentException or a domain-specific exception)
instead of allowing the unchecked exception to propagate silently; update
callers if needed so the scheduler and controller handle the returned/raised
error consistently.
| LocalDateTime nowKst = LocalDateTime.now(ZoneId.of("Asia/Seoul")); | ||
| LocalDateTime expireTime = nowKst.minusMinutes(15); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate TempTicket entity
echo "=== Finding TempTicket entity ==="
fd -e java -path "*/entity/*" | xargs grep -l "class TempTicket" 2>/dev/null || rg -l "class TempTicket" --type=java
echo ""
echo "=== TempTicket entity - createdAt field definition ==="
rg "class TempTicket" -A 50 --type=java | head -100
echo ""
echo "=== Check for Hibernate/JDBC timezone configuration ==="
fd -e properties -o -e yml -o -e yaml | xargs grep -i "time.*zone\|jdbc.time_zone" 2>/dev/null || echo "No explicit timezone config found"
echo ""
echo "=== Check application.yml for timezone settings ==="
fd application.yml -o -path "*application*" -name "*.yml" -o -name "*.yaml" | head -5 | xargs cat 2>/dev/null | grep -i "timezone\|time-zone\|jackson\|jpa\|hibernate" || echo "Checking application.yml..."
echo ""
echo "=== Check for `@CreatedDate`, `@CreationTimestamp`, or `@Temporal` annotations ==="
rg "@CreatedDate|@CreationTimestamp|@Temporal|@Column.*columnDefinition" --type=java -B2 -A2
echo ""
echo "=== Check `@EnableScheduling` configuration ==="
rg "@EnableScheduling" --type=javaRepository: SeeATheater/CC_Backend
Length of output: 14675
🏁 Script executed:
cat -n src/main/java/cc/backend/ticket/entity/TempTicket.javaRepository: SeeATheater/CC_Backend
Length of output: 2642
🏁 Script executed:
cat -n src/main/java/cc/backend/domain/common/BaseEntity.javaRepository: SeeATheater/CC_Backend
Length of output: 896
🏁 Script executed:
find . -name "application*.yml" -o -name "application*.yaml" -o -name "application*.properties" | head -5 | xargs cat 2>/dev/null | head -100Repository: SeeATheater/CC_Backend
Length of output: 2030
🏁 Script executed:
rg "LocalDateTime.now()" --type=java -C2 | head -30Repository: SeeATheater/CC_Backend
Length of output: 3183
Critical timezone mismatch: scheduler uses explicit Asia/Seoul zone while database stores createdAt in JVM default timezone (typically UTC).
LocalDateTime.now(ZoneId.of("Asia/Seoul")) produces a KST-aware timestamp, but the @CreatedDate field in BaseEntity persists createdAt as timezone-unaware LocalDateTime, using the JVM's default timezone (UTC in most cloud deployments). The jackson.time-zone: Asia/Seoul setting in application.yml only affects JSON serialization, not JPA persistence.
When the scheduler compares a KST-based expireTime against UTC-stored createdAt values, timestamps appear ~9 hours older than they actually are, causing tickets to expire prematurely.
Use the same timezone as the persistence layer: either LocalDateTime.now() (matching JVM default) if the database stores in UTC, or explicitly configure hibernate.jdbc.time_zone to ensure consistency.
🐛 Proposed fix (if JVM default is UTC)
- LocalDateTime nowKst = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
- LocalDateTime expireTime = nowKst.minusMinutes(15);
+ LocalDateTime expireTime = LocalDateTime.now().minusMinutes(15);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| LocalDateTime nowKst = LocalDateTime.now(ZoneId.of("Asia/Seoul")); | |
| LocalDateTime expireTime = nowKst.minusMinutes(15); | |
| LocalDateTime expireTime = LocalDateTime.now().minusMinutes(15); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/scheduler/TempTicketExpireScheduler.java` around
lines 27 - 28, The scheduler in TempTicketExpireScheduler uses
LocalDateTime.now(ZoneId.of("Asia/Seoul")) to compute expireTime while persisted
createdAt in BaseEntity is timezone-unaware (JVM default/UTC), causing a 9-hour
mismatch; change the scheduler to use the same timezone as persistence (replace
LocalDateTime.now(ZoneId.of("Asia/Seoul")) and expireTime calculation with
LocalDateTime.now() if your JVM/database uses UTC) or alternatively configure
hibernate.jdbc.time_zone to a fixed zone and make both BaseEntity persistence
and TempTicketExpireScheduler use that same zone consistently; update any
related tests/comments to reflect the chosen approach.
| @Query(""" | ||
| SELECT t | ||
| FROM TempTicket t | ||
| WHERE t.reservationStatus = 'PENDING' | ||
| AND t.createdAt < :expireTime | ||
| """) | ||
| List<TempTicket> findExpiredPendingTickets(LocalDateTime expireTime); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find ReservationStatus enum definition
fd "ReservationStatus.java" --type fRepository: SeeATheater/CC_Backend
Length of output: 133
🏁 Script executed:
# Find TempTicket entity definition
fd "TempTicket.java" --type fRepository: SeeATheater/CC_Backend
Length of output: 120
🏁 Script executed:
# Search for `@Enumerated` annotations in the codebase to understand mapping patterns
rg "@Enumerated" --type java -B 2 -A 2Repository: SeeATheater/CC_Backend
Length of output: 4719
🏁 Script executed:
# Search for any usage of findExpiredPendingTickets method
rg "findExpiredPendingTickets" --type javaRepository: SeeATheater/CC_Backend
Length of output: 350
🏁 Script executed:
# Read ReservationStatus enum definition
cat "src/main/java/cc/backend/ticket/entity/enums/ReservationStatus.java"Repository: SeeATheater/CC_Backend
Length of output: 189
🏁 Script executed:
# Read TempTicket entity definition
cat "src/main/java/cc/backend/ticket/entity/TempTicket.java"Repository: SeeATheater/CC_Backend
Length of output: 2075
🏁 Script executed:
# Read TempTicketExpireScheduler to see usage context
cat "src/main/java/cc/backend/scheduler/TempTicketExpireScheduler.java"Repository: SeeATheater/CC_Backend
Length of output: 1793
Use enum reference instead of string literal for type safety.
The string literal 'PENDING' in JPQL is fragile — if the enum constant name or @Enumerated mapping strategy changes, this query breaks silently at runtime. While the current @Enumerated(EnumType.STRING) mapping makes the comparison work, it lacks compile-time type safety.
Since the method name findExpiredPendingTickets and the scheduler usage indicate that PENDING is intentionally hardcoded (not parameterizable), replace the string literal with a fully qualified enum reference:
`@Query`("""
SELECT t
FROM TempTicket t
- WHERE t.reservationStatus = 'PENDING'
+ WHERE t.reservationStatus = cc.backend.ticket.entity.enums.ReservationStatus.PENDING
AND t.createdAt < :expireTime
""")
List<TempTicket> findExpiredPendingTickets(LocalDateTime expireTime);Alternatively, if the method should accept any status, use a parameter and update the signature:
List<TempTicket> findExpiredPendingTickets(ReservationStatus status, LocalDateTime expireTime);Then pass ReservationStatus.PENDING from the scheduler. However, this changes the API contract—keep the current approach if hardcoding PENDING is the intended design.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/cc/backend/ticket/repository/TempTicketRepository.java` around
lines 39 - 45, The JPQL in TempTicketRepository.findExpiredPendingTickets uses
the string literal 'PENDING' which is brittle; update the query to reference the
enum constant directly (e.g., use the fully qualified enum reference like
YourReservationStatusEnumType.PENDING in the WHERE clause) so the JPQL compares
against the enum constant rather than a string, or if you prefer a reusable API,
change the method signature to accept a ReservationStatus parameter
(ReservationStatus status, LocalDateTime expireTime) and use that parameter in
the WHERE clause and pass ReservationStatus.PENDING from the scheduler; adjust
imports and tests accordingly.
(기존의 프론트 측에서 뒤로가기를 누르면 cancel api 호출하는 방식도 구현해봤으나 너무 복잡한 관계로 백엔드 단에서 스케줄러 처리하기로 했습니다! 문제점: 사용자가 강제로 뒤로가기를 눌렀을때 재고 복구가 바로 반영이 안됨. 15분 이상 지나야 재고 복구)
Summary by CodeRabbit
New Features
Bug Fixes